package org.easyspring.core;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.easyspring.aop.annotation.Aspect;
import org.easyspring.core.annotation.Component;
import org.easyspring.core.annotation.Controller;
import org.easyspring.core.annotation.Repository;
import org.easyspring.core.annotation.Service;
import org.easyspring.util.ClassUtil;
import org.easyspring.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Hy on 2021/7/30
 * 实现容器的加载步骤:
 * 1).配置的管理与获取
 * 2).获取指定范围内的class对象
 * 3).依据配置提取class对象,连同实例一并存入容器
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的Map
     */
    private final Map<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION =
            Arrays.asList(Component.class,Controller.class, Service.class, Repository.class, Aspect.class);
    /**
     * 获取Bean容器实例
     * @return BeanContainer
     */
    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }
    private enum ContainerHolder{
        HOLDER;
        private BeanContainer instance;
        ContainerHolder(){
            instance = new BeanContainer();
        }
    }

    /**
     * 容器是否已经加载过bean
     */
    private boolean loaded = false;
    public boolean isLoad(){
        return loaded;
    }

    /**
     * bean实例的数量
     * @return
     */
    public int size(){
        return beanMap.size();
    }
    /**
     * 扫描加载所有的Bean TODO【002】【★★★★★】
     * @param packageName
     */
    public synchronized void loadBeans(String packageName){
        //判断容器是否加载过
        if(isLoad()){
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if(ValidationUtil.isEmpty(classSet)){
            log.warn("extract nothing from packageName "+packageName);
            return;
        }
        for (Class<?> clazz:classSet
             ) {
            for (Class<? extends Annotation> annotation:BEAN_ANNOTATION
                 ) {
                //如果类上面标记了定义的注解
                if(clazz.isAnnotationPresent(annotation)){
                    //将目标类作为键,目标类的实例作为值,放入到beanMap
                    beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
                }
            }

        }
        loaded = true;
    }

    /**
     * 添加一个class对象及其bean实例
     * @param clazz class对象
     * @param bean bean实例
     * @return 原有的bean实例,没有则返回null
     */
    public Object addBean(Class<?> clazz,Object bean){
        return beanMap.put(clazz,bean);
    }

    /**
     * 移除一个IOC容器管理的对象
     * @param clazz class对象
     * @return 删除的bean实例,没有则返回null
     */
    public Object removeBean(Class<?> clazz){
        return beanMap.remove(clazz);
    }

    /**
     * 根据class对象获取bean实例
     * @param clazz
     * @return
     */
    public Object getBean(Class<?> clazz){
        return beanMap.get(clazz);
    }

    /**
     * 获取容器管理的所有class对象集合
     * @return class集合
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }
    /**
     * 获取所有的bean集合
     * @return bean集合
     */
    public Set<Object> getBeans(){
        return new HashSet<>(beanMap.values());
    }

    /**
     * 根据注解筛选出bean的class集合 TODO【003】【★★★★★】
     * @param annotation 注解
     * @return class集合
     */
    public Set<Class<?>> getClassByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.通过注解筛选出被注解标记的class对象,并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz:keySet
             ) {
            if(clazz.isAnnotationPresent(annotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size()>0 ? classSet : null;
    }

    /**
     * 通过接口或者父类获取实现类或者子类的class集合,不包括其本身 TODO【003】【★★★★★】
     * @param interfaceOrClass 接口class或者父类class
     * @return class集合
     */
    public Set<Class<?>> getClassBySuper(Class<?> interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz:keySet
             ) {
            //判断keySet里的元素是否是传入接口或者类的子类
            //class1.isAssignableFrom(class2) 判定此 Class 对象所表示的类或接口
            // 与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口。
            if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
                classSet.add(clazz);
            }
        }
        return classSet.size()>0 ? classSet : null;
    }
}
